/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package multiplealgo;

import algorithm.Algorithm;
import algorithm.AlgorithmErrorException;
import algorithm.AlgorithmState;
import algorithm.EndReport;
import algorithm.Environment;
import algorithm.GenerationReport;
import algorithm.Population;
import algorithm.PopulationBreeder;
import algorithm.PopulationInitializer;
import algorithm.StopCriterion;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author vermaak
 */
public class HierarchyAlgo implements Algorithm {

    private Population currentPopulation;
    private PopulationInitializer initializer;
    private StopCriterion stopCriterion;
    private PopulationBreeder crossOverBreeder;
    private PopulationBreeder mutationBreeder;
    private AlgorithmState state;
    private Environment environment;
    private List<Algorithm> algorithms;
    private List<GenerationReport> generationReports = new ArrayList<GenerationReport>();
    private List<EndReport> endReports = new ArrayList<EndReport>();

    public HierarchyAlgo(PopulationInitializer initializer, StopCriterion stopCriterion, PopulationBreeder crossOverBreeder, PopulationBreeder mutationBreeder, Environment environment, List<Algorithm> algorithms) {
        this.initializer = initializer;
        this.stopCriterion = stopCriterion;
        this.crossOverBreeder = crossOverBreeder;
        this.mutationBreeder = mutationBreeder;
        this.environment = environment;
        this.algorithms = algorithms;

        state = new AlgorithmState();
    }

    public void run() throws AlgorithmErrorException {
        initialize();

        while (!isDone()) {
            produceNextGeneration();
        }
        for (EndReport endReport : endReports) {
            endReport.write(currentPopulation, environment);
        }
    }

    public void initialize() throws AlgorithmErrorException {
        // populate the local population
        currentPopulation = initializer.createInitialPopulation();
        // populate all the sub-level algos
        for (Algorithm algo : algorithms) {
            algo.initialize();
        }
    }

    public void produceNextGeneration() throws AlgorithmErrorException {
        // produce the next in all the sub algos
        for (Algorithm algo : algorithms) {
            algo.produceNextGeneration();
        }

        // create a new population by crossover with each of the algos below
        for (Algorithm algo : algorithms) {
            currentPopulation = crossOverBreeder.createNewPopulation(currentPopulation, algo.getPopulation(), environment, initializer.getSize());
        }
        // do the usual operations on this population as well
        currentPopulation = crossOverBreeder.createNewPopulation(currentPopulation, currentPopulation, environment, initializer.getSize());
        if (mutationBreeder != null) {
            currentPopulation = mutationBreeder.createNewPopulation(currentPopulation, null, environment, initializer.getSize());
        }

        for (GenerationReport report : generationReports) {
            report.write(currentPopulation, environment);
        }
        state.addGeneration();
    }

    public boolean isDone() {
        return stopCriterion.isDone(state);
    }

    public void addEndReport(EndReport report) {
        endReports.add(report);
    }

    public void addGenerationReport(GenerationReport report) {
        generationReports.add(report);
    }

    public Population getPopulation() {
        return currentPopulation;
    }

    public Environment getEnvironment() {
        return environment;
    }
}
